package com.tyylab.kestrel.framework.spring.web.impl;

import com.tyylab.kestrel.framework.core.annoutil.SpringCoreAnnotationUtil;
import com.tyylab.kestrel.framework.core.annoutil.SpringMVCAnnotationUtil;
import com.tyylab.kestrel.framework.core.build.DefaultClassBuilder;
import com.tyylab.kestrel.framework.core.build.IClassBuilder;
import com.tyylab.kestrel.framework.core.build.ParamEntityClzGenerator;
import com.tyylab.kestrel.framework.core.build.ProxyMethodNameGenerator;
import com.tyylab.kestrel.framework.core.model.anno.params.MVCMappingParams;
import com.tyylab.kestrel.framework.spring.web.util.ConstValue;
import net.bytebuddy.description.annotation.AnnotationDescription;
import org.springframework.http.MediaType;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class ControllerGenerator {
    ControllerConfigGenerator configGenerator;

    ParamEntityClzGenerator paramClzGenerator = new ParamEntityClzGenerator();

    public ControllerGenerator(ControllerConfigGenerator configGenerator){
        this.configGenerator = configGenerator;
    }

    public Class generate(Class<?> api) {
        if (null == api || !api.isInterface())
        {
            return null;
        }

        //build controller class
        IClassBuilder builder = new DefaultClassBuilder();
        AnnotationDescription[] controllerAnnos = generateControllerAnnotation(api);
        String className = api.getName() + ConstValue.ControllerSubFix;
        builder =
                builder.defineClass()
                        .clazz(className)
                        .modifiers(Modifier.PUBLIC)
                        .annotate(controllerAnnos)
                        .end();
        // build properties
        builder = builder.defineProperty()
                .property("api",api, false)
                .annotate(SpringCoreAnnotationUtil.autowired(true))
                .end();
        //build methods
        for (Method method : api.getDeclaredMethods())
        {
            // build params class
            Class<?> paramClass = paramClzGenerator.generateMethodParamClass(method, api);

            AnnotationDescription mappingAnnos = generatePostMappingAnnotation(method);
            AnnotationDescription paramAnno = SpringMVCAnnotationUtil.requestBody(true);

            builder = builder.defineMethod()
                    .method(ProxyMethodNameGenerator.generateMethodName(method),method.getGenericReturnType(),Modifier.PUBLIC)
                    .param("params", paramClass)
                    .end()
                    .impl()
                    // todo :has to refactor
//                    .fixedValue("hellow")
                    .callOnFieldWithParams(method,"api",paramClass)
                    .annotateMethod(mappingAnnos)
                    .annotateParams(0,paramAnno)
                    .end()
                    .end();
        }
        return builder.build();
    }

    AnnotationDescription[] generateControllerAnnotation(Class<?> api) {
        AnnotationDescription[] annotations = new AnnotationDescription[2];
        String path = configGenerator.getControllerPath(api);
        AnnotationDescription annotationDescription = SpringMVCAnnotationUtil.restController(null);
        annotations[0] = annotationDescription;
        // requestmapping
        AnnotationDescription annotationDescription1 = SpringMVCAnnotationUtil.requestMapping(
                new MVCMappingParams(new String[]{path})
        );
        annotations[1] = annotationDescription1;
        return annotations;
    }

    AnnotationDescription generatePostMappingAnnotation(Method method) {
        String path = ProxyMethodNameGenerator.generateMethodName(method);
        AnnotationDescription annotationDescription = SpringMVCAnnotationUtil.postMapping(null,new String[]{path},
                new String[]{}, new String[]{}, null,new String[]{MediaType.APPLICATION_JSON.toString()});
        return annotationDescription;
    }
}
